\section{Nombre}

\texttt{Bench::Test}

Automatización y análisis de ejecuciones.

\section{Sinopsis}

\begin{verbatim}
   use Bench::Test;
\end{verbatim}
\begin{verbatim}
   # New Bench::Test usando un fichero de configuración
   my $bt = Bench::Test->new(
      config_file => 'maketest.conf'
   );
\end{verbatim}
\begin{verbatim}
   # New Bench::Test especificando todos los parámetros a mano
   my $bt = Bench::Test->new(
       tests       => \@TESTS,
       table       => \@TABLE,
       visual      => \@VISUAL,
       experiments => \%EXPERIMENTS,
       selected    => \@SELECTED,
   );
\end{verbatim}
\begin{verbatim}
   # Executando "make test"
   $bt->make_test;
\end{verbatim}
\begin{verbatim}
   # Executando "create test"
   $bt->create_tests;
\end{verbatim}

\section{Descripción}

El objeto Bench::Test permite automatizar, analizar y comparar
diversas ejecuciones realizadas en tiempo real o en el pasado y 
en la máquina local y/o máquinas remotas simultáneamente. Cuenta con
dos métodos principales: \texttt{make\_test} y \texttt{create\_tests}.

Los parámetros de configuración de las ejecuciones pueden 
definirse mediante un fichero de configuración (por defecto,
maketest.conf) o mediante parámetros en el constructor.

La salida de las ejecuciones es filtrada mediante un
conjunto de expresiones regulares indicadas por el usuario.
El resultado es devuelto al usuario por pantalla.

\subsection{Un ejemplo}

Supongamos un programa cuya salida sea la siguiente:

\begin{verbatim}
   ~/projects/Bench-Test/examples/mm$ ./mm dat/mm_200.dat 
   Filas primera matriz: 200
   Filas segunda matriz: 200
   Columnas segunda matriz: 200
   Time of product: 0.210942
   Result: 4102
\end{verbatim}

Supongamos que queremos ejecutar dicho programa con
diferentes parámetros de entrada y comparar las salidas.

La idea es que el usuario especifique las características
del experimento y ejecute el método \texttt{make\_test}. El método
realizará las ejecuciones para cada conjunto de parámetros,
analizará las salidas, las formateará y las devolverá al usuario
por pantalla o sobre ficheros.

Ejecutando el script \texttt{maketest}:

\begin{verbatim}
   #!/usr/bin/perl -w
\end{verbatim}
\begin{verbatim}
   use strict;
   use Pod::Usage;
   use Getopt::Long;
   use Bench::Test;
   use Carp;
\end{verbatim}
\begin{verbatim}
   # MAIN ======================
\end{verbatim}
\begin{verbatim}
   my $config_file = './maketest.conf';   # By default is 'maketest.conf'
\end{verbatim}
\begin{verbatim}
   my $bench = new Bench::Test(
     config_file => $config_file,
   );
\end{verbatim}
\begin{verbatim}
   $bench->make_tests;
\end{verbatim}

Se obtiene una salida similar a la siguiente:

\begin{verbatim}
   ~/projects/Bench-Test/examples/mm$ maketest
   TEST    FILE    DIM             TIME(BASE_feb)  TIME(LOCAL)     TIME(ORION)
   OK      mm_100  100:100:100     0.011588        0.026716        0.011582
   OK      mm_200  200:200:200     0.091474        0.202479        0.092152
   OK      mm_300  300:300:300     0.432486        1.000445        0.432919
   OK      mm_500  500:500:500     3.923428        5.067983        2.547144
\end{verbatim}

\section{Métodos}
\subsection{El constructor \texttt{new}}

Véase una llamada típica:

\begin{verbatim}
   # New Bench::Test usando un fichero de configuración
   my $bt = Bench::Test->new(
      config_file => 'maketest.conf'
   );
\end{verbatim}
\begin{verbatim}
   # New Bench::Test especificando todos los parámetros a mano
   my $bt = Bench::Test->new(
       tests       => \@TESTS,
       table       => \@TABLE,
       visual      => \@VISUAL,
       experiments => \%EXPERIMENTS,
       selected    => \@SELECTED,
   );
\end{verbatim}

Este método devuelve una nueva instancia de un objeto
\texttt{Bench::Test}.

Los parámetros de configuración pueden ser expresados
en un fichero de con\-fi\-gu\-ra\-ción, con parámetros
del constructor o con ambos. En caso de conflicto
tienen mayor prioridad los parámetros definidos en 
el constructor y no en el fichero de configuración.

Los nombres de los parámetros son exactamente los 
mismos en el constructor y en el fichero de configuración
excepto que en el fichero se escriben en mayúsculas.

\subsection{El método \texttt{make\_test}}

El método \texttt{make\_test} se encarga de automatizar las ejecuciones
definidas previamente por el usuario. A su vez, analiza las salidas
y las formatea para mostrarlas al usuario y/o almacenarlas en 
ficheros de texto con distintos formatos.

\subsection{El método \texttt{create\_tests}}

El método \texttt{create\_tests} solamente automatiza las ejecuciones
almacenando las salidas en ficheros de texto nombrados con
extensión \texttt{.base} .

\section{El fichero de configuración}

Véase un ejemplo de fichero de configuración:

\begin{verbatim}
   ~/projects/Bench-Test/examples/mm$ cat maketest.conf
\end{verbatim}
\begin{verbatim}
   @TESTS = (
      mm_100 => './dat/mm_100.dat',
      mm_200 => './dat/mm_200.dat',
      mm_300 => './dat/mm_300.dat',
     mm_500 => './dat/mm_500.dat',
   );
\end{verbatim}
\begin{verbatim}
   @TABLE         = (
      #TEST metafield
      #FILE metafield
\end{verbatim}
\begin{verbatim}
      DIM  => 'primera\s*matriz:\s*(.*)\n',
      RES  => 'Result:\s+(.*)\n', 
      TIME => 'Time of product:\s*(.*)\n',
   );
\end{verbatim}
\begin{verbatim}
   @UNIQUE   = qw(DIM);
   @VISUAL   = qw(TEST FILE DIM TIME);
   @SELECTED = qw(BASE_feb LOCAL ORION);
\end{verbatim}
\begin{verbatim}
   sub Bench::Test::Entry::is_OK { Code to check if test is OK }
\end{verbatim}
\begin{verbatim}
   %EXPERIMENTS = ( 
      'BASE_feb' => Bench::Test::Base->new ( 
                     WORKDIR   => './base_FEB'
                  ),
      'LOCAL'    => Bench::Test::Local->new  (
                     WORKDIR   => '.',
                     EXECUTABLE => './mm',
                     OUTPUTFILE => 'output.dat',
                     BASEDIR   => './base_FEB',
                  ),
      'ORION'    => Bench::Test::Remote->new (
                     HOST       => 'user@host',
                     WORKDIR    => '/home/user/mm',
                     EXECUTABLE => 'mm',
                     BASEDIR    => './base_ORION',
                     OUTPUTFILE => 'output.dat',
                     PREAMBLE   => sub {
                        my $machine = shift;
                        $machine->put(['mm.tar.gz']);
                        $machine->tar('-xf', 'mm.tar.gz')->ok
                           or die 'Error when extracting mm.tar.gz';
                        $machine->chdir('mm');
                        $machine->system('make');
                     },
                     POSTAMBLE  => sub {
                        my $machine = shift;
                        $machine->chdir('..');  
                        $machine->system('rm -rf mm');
                        $machine->system('rm mm.tar.gz');
                    },
                  ),
   );
\end{verbatim}
\subsection{El array \texttt{@TABLE}}

El array \texttt{@TABLE} se utiliza para especificar las
expresiones regulares definiendo las partes de la salida en 
las que se está interesado. La clave es el nombre del campo
(\texttt{DIM}, \texttt{TIME}, etc. en el ejemplo).

Adicionalmente puede incluirse una referencia a rutina configurable
por el usuario que permite una mayor versatilidad. 

\begin{verbatim}
   @TABLE         = (
      #TEST metafield
      #FILE metafield
\end{verbatim}
\begin{verbatim}
      DIM  => 'primera\s*matriz:\s*(.*)\n'
      RES  => 'Result:\s+(.*)\n',
      TIME => 'Time of product:\s*(.*)\n',
   );
\end{verbatim}

\subsection{El array \texttt{@TESTS}}

El array \texttt{@TESTS} contiene un par clave-valor por cada uno
de los tests que se desea realizar. Cada entrada está formada por una cadena
que indica los parámetros con los que se ejecuta el programa 
ejecutable que se está analizando (valor) y el nombre del test (clave). Ejemplo:

\begin{verbatim}
   @TESTS = (
      mm_100 => './dat/mm_100.dat',
      mm_200 => './dat/mm_200.dat',
      mm_300 => './dat/mm_300.dat',
      mm_500 => './dat/mm_500.dat',
   );
\end{verbatim}

Se proveen dos rutinas de apoyo al usuario que permiten definir
el array \texttt{@TESTS} mediante el contenido de un fichero o mediante
un \textit{glob}.

La siguiente sentencia lee el fichero \texttt{myfile.test} línea a línea
obteniendo los pares (clave,valor) separados por ':'

\begin{verbatim}
  @TESTS = get_tests_from_file('myfile.test')
\end{verbatim}

Si el contenido del fichero \texttt{myfile.test} fuera el siguiente:

\begin{verbatim}
   ~/projects/Bench-Test/examples/mm$ cat myfile.test
   test1:-n 100 test1.dat
   test2:-n 200 test2.dat
   test3:-n 300 test2.dat
\end{verbatim}

El resultado de \texttt{get\_tests\_from\_file} y, por tanto, el contenido
del array \texttt{@TESTS} sería:

\begin{verbatim}
   @TESTS = (
      test1 => '-n 100 test1.dat',
      test2 => '-n 200 test2.dat',
      test3 => '-n 300 test3.dat'
   );
\end{verbatim}

La rutina \texttt{get\_tests\_from\_glob} supone que el programa recibe
un solo parámetro: el nombre de un fichero. Por tanto, suponiendo
un directorio como el siguiente:

\begin{verbatim}
   ~/projects/Bench-Test/examples/mm/dat$ ls -l
   total 1548
   -rw-r--r-- 1 alu2807 alu2807   40212 2008-02-11 09:36 mm_100.dat
   -rw-r--r-- 1 alu2807 alu2807  160412 2008-02-11 09:36 mm_200.dat
   -rw-r--r-- 1 alu2807 alu2807  360612 2008-02-11 09:36 mm_300.dat
   -rw-r--r-- 1 alu2807 alu2807 1001012 2008-02-11 09:36 mm_500.dat
\end{verbatim}

Y una llamada como la siguiente:

\begin{verbatim}
   @TESTS = get_tests_from_glob('*.dat');
\end{verbatim}

Se obtendría el siguiente array \texttt{@TESTS}:

\begin{verbatim}
   @TESTS = (
      mm_100 => 'mm_100.dat',
      mm_200 => 'mm_200.dat',
      mm_300 => 'mm_300.dat',
      mm_500 => 'mm_500.dat'
   );
\end{verbatim}
\subsection{El array \texttt{@VISUAL}}

El array \texttt{@VISUAL} determina qué campos serán mostrados 
por pantalla y en qué orden. Los nombres de los campos
son las claves definidas en el
array \texttt{@TABLE} (\texttt{DIM}, \texttt{TIME}, etc.). Ejemplo:

\begin{verbatim}
  @VISUAL = qw(TEST FILE DIM TIME);
\end{verbatim}

Dicha definición produciría una salida como ésta:

\begin{verbatim}
  ~/projects/Bench-Test/mm$ ./maketest 
  TEST    FILE    DIM     TIME(BASE_feb)  TIME(LOCAL)     TIME(ORION)
  OK      mm_100  100:100:100     0.014589        0.013538        0.009383
  OK      mm_200  200:200:200     0.109944        0.177367        0.073903
  OK      mm_300  300:300:300     0.361250        0.528413        0.359595
  OK      mm_500  500:500:500     1.934884        2.993155        1.886891
\end{verbatim}


Obsérvese que algunos de los campos están repetidos para
cada uno de los \texttt{EXPERIMENTS} y otros no. Esto es así, porque
existen campos que son invariables cualquiera que sea la máquina
donde se ejecute. Por ejemplo: la entrada de problema, las dimensiones.

Para indicar qué campos no deben duplicarse por tratarse de campos únicos
se utiliza el array \texttt{UNIQUE}.

\subsection{El array \texttt{@UNIQUE}}

Indica los campos para los que el resultado de la ejecución no varía
de una máquina a otra. Ejemplo:

\begin{verbatim}
  @UNIQUE = qw(DIM);
\end{verbatim}

\subsection{El array \texttt{@SELECTED}}

Indica los experimentos que serán ejecutados. Similar al array
\texttt{@VISUAL} pero con los experimentos.

\subsection{El hash \texttt{\%EXPERIMENTS}}

Este hash almacena la información acerca de las distintas máquinas remotas,
máquina local y resultados de ejecuciones anteriores.

La clave es el nombre que se muestra al usuario. El valor puede ser uno de 
los siguientes objetos:

\begin{itemize}
\item \texttt{Bench::Test::Base}
\item \texttt{Bench::Test::Local}
\item \texttt{Bench::Test::Remote}
\end{itemize}

Los tres objetos cuentan con constructores distintos, pero dos métodos
idénticos, con la misma interfaz:

\begin{itemize}
\item \texttt{get\_result  (\$params, \$test)}
\item \texttt{save\_result (\$params, \$test)}
\end{itemize}

Estos métodos se encargan de devolver el resultado de la ejecución
del programa del usuario con los parámetros especificados en \texttt{\$params}
(\texttt{get\_result}) o almacenarlo en el correspondiente archivo \texttt{.base} (\texttt{save\_result}).

El parámetro \texttt{\$test} se utiliza únicamente en el objeto \texttt{Bench::Test::Base}
e indica el nombre del test al que se quiere acceder. El resultado de
la salida de una ejecución anterior estara almacenado en el fichero \texttt{\$test.base}
dentro del directorio \texttt{WORKDIR} de dicho objeto.

\subsection{El objeto \texttt{Bench::Test::Base}}

Su constructor acepta un único parámetro (\texttt{WORKDIR}) y es obligatorio. Este parámetro
indica el directorio donde se encuentran los fichero \texttt{.base} (salidas de
las ejecuciones almacenadas).

\subsection{El objeto \texttt{Bench::Test::Local}}

Su constructor acepta los siguientes parámetros:

\begin{description}

\item[{\texttt{WORKDIR}}] \mbox{}

Indica el directorio de trabajo del proyecto.


\item[{\texttt{EXECUTABLE}}] \mbox{}

Nombre del fichero ejecutable. Ruta relativa a partir
del directorio de trabajo.

\item[{\texttt{BASEDIR}}] \mbox{}

Opcional. Indica el directorio donde se almacenaran
los ficheros \texttt{.base} en caso de querer crearlo.

\item[{\texttt{OUTPUTFILE}}] \mbox{}

Opcional. Nombre del fichero de donde se leerá la
salida del programa. Si no se especifica, se toma
la salida por \texttt{stdout}.

\end{description}

\subsection{El Objeto \texttt{Bench::Test::Remote}}

Su constructor acepta los siguientes parámetros:

\begin{description}

\item[{\texttt{WORKDIR}}] \mbox{}

Mismo significado que para \texttt{Bench::Test::Local}.


\item[{\texttt{EXECUTABLE}, \texttt{BASEDIR}, \texttt{OUTPUTFILE}}] \mbox{}

Mismo significado que para \texttt{Bench::Test::Local}.


\item[{\texttt{HOST}}] \mbox{}

Usuario y host de la máquina remota con el formato \texttt{user@host}.

\item[{\texttt{PREAMBLE}}] \mbox{}

Rutina que será ejecutada antes de realizar los tests. Se
ejecuta en la máquina local, pero se le pasa un objeto de tipo
\texttt{GRID::Machine} conectado a la máquina remota, con lo que se le
permite al usuario aprovechar toda su funcionalidad.

\item[{\texttt{POSTAMBLE}}] \mbox{}

Idéntico a \texttt{PREAMBLE} salvo que se ejecuta después de haber
realizado todos los tests.

\end{description}

\section{Véase también}

Para una mejor comprensión del módulo \texttt{Bench::Test}, véase también la
documentación de los siguientes módulos:

\begin{itemize}
\item \texttt{Bench::Test::Base}
\item \texttt{Bench::Test::Local}
\item \texttt{Bench::Test::Remote}
\item \texttt{Bench::Test::Entry}
\item \texttt{Bench::Test::View}
\item \texttt{GRID::Machine} 
\end{itemize}
